home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 4
/
Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso
/
Development
/
Source
/
DBL Pascal Library
/
INIT Shell Folder
/
INIT Shell ƒ
/
Shell ƒ
/
INITShellLoader.p
< prev
next >
Wrap
Text File
|
1990-06-24
|
12KB
|
409 lines
unit INITShellLoader;
{Copyright © 1990, David B. Lamkins}
{All rights reserved.}
interface
uses
Retrace, SysEqu, INITDoInstall;
procedure main; {INIT entry point}
implementation
{•• There is no code here which needs to be modified.}
{•• All of your INIT programming should take place in the INITDoInstall unit.}
{This is the main code of the INIT loader. Variables of main are pseudo-globals for all of}
{its subroutines.}
procedure main;
const
trapNumMask = $1FF; {low 9 bits of trap opcode contain trap number}
ResidentType = 'IRES'; {the resource type of patches and VBLs loaded by this INIT}
loadOK = 128; {ICN# for "normal successful installation"}
loadSkipped = 129; {ICN# for "the INIT was skipped because the mouse button was down"}
loadFailed = 130; {ICN# for "something has gone wrong"}
loadNotDone = 131; {ICN# for "not loaded due to configuration"}
type
PatchInfo = record
trapOpCode: INTEGER;
oldAddress: LONGINT;
patchHandle: Handle;
end;
PatchInfoArray = array[1..1] of PatchInfo;
PatchInfoArrayPtr = ^PatchInfoArray;
VBLInfo = record
queuePtr: QElemPtr;
taskHandle: Handle;
end;
VBLInfoArray = array[1..1] of VBLInfo;
VBLInfoArrayPtr = ^VBLInfoArray;
QDGlobals = record
private: packed array[1..202] of Byte;
thePort: GrafPtr;
end;
LongPtr = ^LONGINT;
InstallState = (OK, Failed, Cancelled);
var
PatchesInfoPtr: PatchInfoArrayPtr;
VBLsInfoPtr: VBLInfoArrayPtr;
NumPatches, NumVBLs: INTEGER;
MaxPatches, MaxVBLs: INTEGER;
theState: InstallState;
EnableIcon: BOOLEAN;
theINITGlobals: Handle;
saveA5: LONGINT;
myPort: GrafPort;
localQD: QDGlobals;
localA5: LONGINT;
{This procedure is called at patch/VBL installation time to stash the globals handle into the}
{modified code header for the patch/VBL code. The handle is created by StartInstall. Additionally,}
{the code header is modified to allow a jump to a patched trap by using SetTrapExit prior to}
{returning from the patch code.}
procedure SetGlobalPtr (codeAddress: Ptr; trapAddress: LONGINT);
type
CodeHeader = record
BSR_instruction: INTEGER; {BSR.S *+$10}
RTS_instruction: INTEGER; {RTS}
JMP_opcode: INTEGER; {JMP patched_routine}
JMP_address: LONGINT;
LockCount: INTEGER; {number of times globals have been locked}
GlobalH: Handle; {handle to our globals}
end;
CodeHeaderPtr = ^CodeHeader;
begin
with CodeHeaderPtr(codeAddress)^ do
begin
BSR_instruction := $610E;
RTS_instruction := $4E75;
JMP_opcode := $4EF9;
JMP_address := trapAddress;
LockCount := 0;
GlobalH := theINITGlobals;
end;
end;
{Here is how we remember the things we'll need to undo a trap patch…}
procedure RememberPatch (h: Handle; trap: INTEGER; originalAddress: LONGINT);
begin
numPatches := numPatches + 1;
if numPatches <= MaxPatches then
{$PUSH}
{$R-}
with PatchesInfoPtr^[numPatches] do
begin
patchHandle := h;
trapOpCode := trap;
oldAddress := originalAddress;
end;
{$POP}
end;
{This corresponds to the previous routine, but for VBL tasks…}
procedure RememberVBL (h: Handle; q: QElemPtr);
begin
NumVBLs := NumVBLs + 1;
if NumVBLs <= MaxVBLs then
{$PUSH}
{$R-}
with VBLsInfoPtr^[NumVBLs] do
begin
taskHandle := h;
queuePtr := q;
end;
{$POP}
end;
{This is a pre-initialization to make sure the loader stays sane even if we forget to call}
{DoInstall(initCallback,…).}
procedure StartInstall;
begin
theState := Failed;
NumPatches := 0;
MaxPatches := 0;
PatchesInfoPtr := PatchInfoArrayPtr(NewPtr(0));
NumVBLs := 0;
MaxVBLs := 0;
VBLsInfoPtr := VBLInfoArrayPtr(NewPtr(0));
theINITGlobals := NewHandleSysClear(0);
end;
{Here is where we initalize the state of the INIT Shell Loader. Also, we clear the DeskHook}
{and the DragHook per Apple's Tech Note 247.}
procedure InitInstaller (patchLimit, VBLLimit, sizeOfGlobals: INTEGER);
begin
theState := OK;
EnableIcon := TRUE;
MaxPatches := patchLimit;
DisposPtr(Ptr(PatchesInfoPtr));
PatchesInfoPtr := PatchInfoArrayPtr(NewPtr(SIZEOF(PatchInfo) * MaxPatches));
MaxVBLs := VBLLimit;
DisposPtr(Ptr(VBLsInfoPtr));
VBLsInfoPtr := VBLInfoArrayPtr(NewPtr(SIZEOF(VBLInfo) * MaxVBLs));
SetHandleSize(theINITGlobals, sizeOfGlobals);
LongPtr(DeskHook)^ := 0; {according to TN 247}
LongPtr(DragHook)^ := 0; {ditto}
end;
{Before we finish installation, we call this to dispose the pointers used internally.}
procedure EndInstall;
begin
DisposPtr(Ptr(PatchesInfoPtr));
DisposPtr(Ptr(VBLsInfoPtr));
end;
{If something goes wrong during the installation, we call AbortInstall to unhook all the trap}
{patches and VBL tasks that had been installed by the loader.}
procedure AbortInstall;
var
i: INTEGER;
error: OSErr;
theTrapKind: TrapType;
begin
for i := 1 to NumVBLs do
{$PUSH}
{$R-}
with VBLsInfoPtr^[i] do
begin
error := VRemove(queuePtr);
DisposHandle(taskHandle);
end;
{$POP}
for i := 1 to NumPatches do
{$PUSH}
{$R-}
with PatchesInfoPtr^[i] do
begin
if BTST(trapOpCode, 11) then
theTrapKind := ToolTrap
else
theTrapKind := OSTrap;
NSetTrapAddress(oldAddress, BAND(trapNumMask, trapOpCode), theTrapKind);
DisposHandle(patchHandle);
end;
{$POP}
DisposHandle(theINITGlobals);
end;
{Here is where we actually install a trap patch. The patch code is referenced by resource ID}
{(for a resource of type ResidentType, which is normally IRES). The trap is referenced by}
{its full opcode, not just the number (i.e. _Open = $A000). Note that if something goes wrong,}
{this fact is reflected in the loader global theState, and future patch installs are automatically}
{skipped.}
procedure InstallPatch (id, trap: INTEGER);
var
thePatch: Handle;
theGlobalsHandle: Handle;
theTrapAddress: LONGINT;
theTrapKind: TrapType;
begin
if theState = OK then
begin
thePatch := GetResource(ResidentType, id);
if thePatch <> nil then
begin
HLock(thePatch);
if BTST(trap, 11) then
theTrapKind := ToolTrap
else
theTrapKind := OSTrap;
theTrapAddress := NGetTrapAddress(trap, theTrapKind);
SetGlobalPtr(thePatch^, theTrapAddress);
NSetTrapAddress(ORD(thePatch^), BAND(trapNumMask, trap), theTrapKind);
DetachResource(thePatch);
RememberPatch(thePatch, trap, theTrapAddress);
end
else
theState := Failed;
end;
end;
{The installer for VBL tasks is similar to the trap patch installer…}
procedure InstallVBL (id, count, phase: INTEGER);
type
VBLTaskPtr = ^VBLTask;
var
theTask: Handle;
theGlobalsHandle: Handle;
theVBLTask: VBLTaskPtr;
error: OSErr;
begin
if theState = OK then
begin
theTask := GetResource(ResidentType, id);
if theTask <> nil then
begin
HLock(theTask);
SetGlobalPtr(theTask^, 0);
theVBLTask := VBLTaskPtr(NewPtrSys(SIZEOF(VBLTask)));
with theVBLTask^ do
begin
qType := ORD(vType);
vblAddr := theTask^;
vblCount := count;
vblPhase := phase;
end;
error := VInstall(QElemPtr(theVBLTask));
DetachResource(theTask);
RememberVBL(theTask, QElemPtr(theVBLTask));
end
else
theState := Failed;
end;
end;
{Simple inline routines to directly manipulate register A5…}
function RegA5: LONGINT;
inline
$2E8D; {MOVE.L A5,(SP)}
procedure SetA5 (where: Ptr);
inline
$2A5F; {MOVEA.L (SP)+,A5}
{Create a Quickdraw world for use by the INIT loader.}
procedure ConstructQD;
begin
saveA5 := RegA5;
SetA5(@localA5);
LongPtr(CurrentA5)^ := LONGINT(@localA5);
InitGraf(@localQD.thePort);
OpenPort(@myPort);
end;
{Destroy the INIT loader's Quickdraw world.}
procedure DestructQD;
begin
ClosePort(@myPort);
SetA5(Pointer(saveA5));
LongPtr(CurrentA5)^ := saveA5;
end;
{Calculate the check value for the INIT icon's horizontal position.}
function CheckH (n: INTEGER): INTEGER;
inline
$301F, {MOVE.W (SP)+,D0}
$E358, {ROL.W #1,D0}
$0A40, $1021, {EORI.W #$1021,D0}
$3E80; {MOVE.W D0,(SP)}
{Plot the INIT's icon in a manner compatible with the standard ShowInit routine.}
procedure DisplayIcon (id: INTEGER);
const
HOffsetAddr = CurApName + 28;
CheckAddr = CurApName + 30;
hOffset = 40;
vOffset = 40;
iconResType = 'ICN#';
type
ICN = record
data: array[1..32] of LONGINT;
mask: array[1..32] of LONGINT;
end;
ICNPtr = ^ICN;
ICNHandle = ^ICNPtr;
IntPtr = ^INTEGER;
OSTypePtr = ^OSType;
var
theIcon: ICNHandle;
srcRect, dstRect: Rect;
myBitMap: BitMap;
begin
if EnableIcon then
begin
theIcon := ICNHandle(GetResource(iconResType, id));
if theIcon <> nil then
begin
HLock(Handle(theIcon));
if CheckH(IntPtr(HOffsetAddr)^) <> IntPtr(CheckAddr)^ then
IntPtr(HOffsetAddr)^ := 8;
with myPort, dstRect do
begin
top := portRect.bottom - vOffset;
left := IntPtr(HOffsetAddr)^;
bottom := top + 32;
right := left + 32;
end;
with myBitMap do
begin
baseAddr := @theIcon^^.mask;
rowBytes := 4;
SetRect(bounds, 0, 0, 32, 32);
end;
SetRect(srcRect, 0, 0, 32, 32);
CopyBits(myBitMap, myPort.portBits, srcRect, dstRect, srcBic, nil);
myBitMap.baseAddr := @theIcon^^.data;
CopyBits(myBitMap, myPort.portBits, srcRect, dstRect, srcOr, nil);
IntPtr(HOffsetAddr)^ := IntPtr(HOffsetAddr)^ + hOffset;
IntPtr(CheckAddr)^ := CheckH(IntPtr(HOffsetAddr)^);
ReleaseResource(Handle(theIcon));
end;
end;
end;
{This is a callback routine. It is passed as a parameter to the INIT-specific installation}
{routine, DoInstall. The callback provides a standard way for DoInstall to access routines}
{and globals in the loader unit.}
procedure InstallCallback (func: callbackCode; param1, param2, param3: INTEGER);
begin
case func of
initCallback:
InitInstaller(param1, param2, param3); {maxPatch,maxVBL,globalsSize}
setPatch:
InstallPatch(param1, param2); {resID, trapWord}
setVBL:
InstallVBL(param1, param2, param3); {resID, count, phase}
failInstall:
theState := Failed;
doNotInstall:
theState := Cancelled;
suppressIcon:
EnableIcon := FALSE;
otherwise
theState := Failed;
end;
end;
{Here's the main flow of the loader. Note that the INIT's icon is not displayed until after}
{DoInstall returns, thus allowing the icon to graphically indicate the success or failure of}
{the installation. Note also that the standard "mouse-button-down" means of skipping the}
{INIT loader is implemented here, and allows for its own distinctive icon. Finally, note that}
{a valid Quickdraw environment is available during the entire loading process.}
begin {main}
ConstructQD; {Create a local QuickDraw environment.}
if Button then
DisplayIcon(loadSkipped)
else
begin
StartInstall;
{The interesting INIT code is all in DoInstall - you write it…}
DoInstall(InstallCallback);
{Upon return, if everthing's OK we display the "normal" icon.}
if theState = OK then
DisplayIcon(loadOK)
else
{Something went wrong during installation - either a missing resource, or a}
{program-detected error which was cause to skip or abort loading the INIT.}
begin
{Make sure our patches and VBLs get unhooked, then indicate reason w/icon.}
AbortInstall;
if theState = Cancelled then
DisplayIcon(loadNotDone)
else
DisplayIcon(loadFailed);
end;
EndInstall; {Clean up installer environment.}
end;
DestructQD; {Get rid of our QuickDraw environment.}
end;
end.